/*
 * Copyright (c) 2025 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
import {ArkFile, ts} from 'arkanalyzer';
import {BaseChecker, BaseMetaData} from '../BaseChecker';
import {Defects, IssueReport} from '../../model/Defects';
import {RuleFix} from '../../model/Fix';
import {Rule} from '../../model/Rule';
import {AstMatcher, MatcherCallback, MatcherTypes} from '../../matcher/Matchers';
import {RuleListUtil} from "../../utils/common/DefectsList";

const gMetaData: BaseMetaData = {
    severity: 2,
    ruleDocPath: 'docs/prefer-namespace-keyword.md',
    description: "Use 'namespace' instead of 'module' to declare custom TypeScript modules.",
};

export class PreferNamespaceKeywordCheck implements BaseChecker {
    readonly metaData: BaseMetaData = gMetaData;
    public rule: Rule;
    public defects: Defects[] = [];
    public issues: IssueReport[] = [];
    private buildMatcher: AstMatcher = {
        matcherType: MatcherTypes.AST,
        match(node: ts.Node): boolean {
            return ts.isModuleDeclaration(node);
        }
    };

    public registerMatchers(): MatcherCallback[] {
        const matchBuildCb: MatcherCallback = {
            matcher: this.buildMatcher,
            callback: this.check
        };
        return [matchBuildCb];
    };

    public check = (node: ts.ModuleDeclaration, arkFile: ArkFile): void => {
        this.processModuleDeclaration(node, arkFile);
    };

    private processModuleDeclaration(node: ts.ModuleDeclaration, arkFile: ArkFile): void {
        const moduleTokens = Array.from(node.getChildren());
        const moduleKeywordNode = moduleTokens.find(token =>
            token.kind === ts.SyntaxKind.ModuleKeyword);
        if (node.name.kind !== ts.SyntaxKind.Identifier) {
            return;
        }
        if (moduleKeywordNode && moduleKeywordNode.getText() === "module") {
            // 创建缺陷报告和修复建议
            let sourceFile = node.getSourceFile();
            let defect = this.createDefect(arkFile, sourceFile, moduleKeywordNode, node);
            const fixKeyword = "namespace";
            let ruleFix = this.createFix(moduleKeywordNode, fixKeyword);
            this.issues.push(new IssueReport(defect, ruleFix));
        }
    };

    private createDefect(arkFile: ArkFile, sourceFile: ts.SourceFile, moduleKeywordNode: ts.Node, node: ts.ModuleDeclaration): Defects {
        const filePath = arkFile.getFilePath();
        let start = node.getStart(sourceFile);
        const end = moduleKeywordNode.getEnd();

        if (node.modifiers?.some((modifier) => modifier.kind === ts.SyntaxKind.ExportKeyword)) {
            start = moduleKeywordNode.getStart(sourceFile);
        }

        // 获取moudle行列位置
        const startPosition = sourceFile.getLineAndCharacterOfPosition(start);
        const endPosition = sourceFile.getLineAndCharacterOfPosition(end);

        const severity = this.rule.alert ?? this.metaData.severity;
        const defect = new Defects(
            startPosition.line + 1,
            startPosition.character + 1,
            endPosition.character + 1,
            this.metaData.description,
            severity,
            this.rule.ruleId,
            filePath,
            this.metaData.ruleDocPath,
            true,
            false,
            true
        );

        this.defects.push(defect);
        RuleListUtil.push(defect);
        return defect;
    };

    private createFix(child: ts.Node, code: string): RuleFix {
        return {range: [child.getStart(), child.getEnd()], text: code};
    };
}